#pragma once

#include "index.hpp"
#include <boost/algorithm/string.hpp>
#include "util.hpp"
#include "jsoncpp/json/json.h"
#include <iostream>
#include <algorithm>
#include"log.hpp"

namespace ns_searcher
{
    //将一个id对应的关键词整合在一起
    struct inventPrint
    {
        uint64_t _id;
        int _weight;
        std::vector<std::string> _words;
        inventPrint()
            : _weight(0)
        {
        }
    };
    class searcher
    {
    public:
        //需要建立好正排和倒排索引
        void initSearcher(const std::string &input)
        {
            //获取index对象
            _index = ns_index::index::getInstance();
            LOG(NORMAL,"init index success");
            //创建索引
            _index->buildIndex(input);
        }

        void search(const std::string &query, std::string *json_string)
        {
            // 1.根据jieba切分搜索词
            std::string q = query;
            boost::to_lower(q);
            std::vector<std::string> words;
            ns_util::jieBaUtil::curString(q, &words);

            std::unordered_map<uint64_t, inventPrint> tokenMap;
            //为了防止关键词对应到1个文档id，所以使用哈希去重
            // 2.根据关键词查找倒排拉链
            std::vector<inventPrint> results; //临时保存查找出的元素,方便进行后期的排序与列举
            for (auto &s : words)
            {
                ns_index::invertedList *ivl = _index->getInvertedElem(s);
                if (ivl)
                {
                    //将所有查找出的拉链插入表中
                    //ivl已经将所有的单词和文档对应了起来
                    //因为原始数据已经记录了每个关键词所对应的id
                    //所以这里用id就能找到关键词
                    for (auto &i : *ivl)
                    {
                        auto &ivp = tokenMap[i._id];
                        ivp._id = i._id;
                        ivp._weight += i._weight;
                        ivp._words.push_back(i._word);
                    }
                }
            }

            for (auto &i : tokenMap)
            {
                results.push_back(i.second);
            }

            // 3.排序，根据权重降序排序
            sort(results.begin(), results.end(), [](const inventPrint &e1, const inventPrint &e2)
                 { return e1._weight > e2._weight; });
            // 4.用jsoncpp序列化

            Json::Value root;

            for (auto &i : results)
            {
                ns_index::DocInfo_t *doc = _index->getForwardElem(i._id); //找到对应的文档描述
                if (doc)
                {
                    Json::Value item;
                    item["title"] = doc->_title;
                    item["body"] = getDesc(doc->_body, i._words[0]); //获取摘要
                    item["url"] = doc->_url;
                    // for debug
                    // item["weight"] = i._weight;
                    // item["id"] = (int)i._id;
                    root.append(item);
                }
            }

            Json::FastWriter writer;
            *json_string = writer.write(root);
        }

    private:
        ns_index::index *_index;

        std::string getDesc(const std::string &body, const std::string &key)
        {
            int begin = 0;
            int end = body.size() - 1;

            auto iter = std::search(body.begin(), body.end(), key.begin(), key.end(),
                                    [](int x, int y)
                                    {
                                        return std::tolower(x) == std::tolower(y);
                                    }); //转为小写进行比较，与反向索引匹配

            if (iter == body.end())
            {
                return "Not Found";
            }

            int index = std::distance(body.begin(), iter);
            if (index - 50 >= begin)
            {
                begin = index;
            }

            if (index + 100 < end)
            {
                end = index + 100;
            }

            if (begin > end)
            {
                return "Out Range";
            }

            if (begin < 0 || end < 0)
            {
                return "?";
            }

            return body.substr(begin, end - begin);
        }
    };

} // namespace ns_searcher
